﻿using System;
using System.Collections.Generic;
using System.Collections.Specialized;
using System.ComponentModel;
using System.Configuration;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Controls.Primitives;
using System.Windows.Data;
using System.Windows.Forms;
using System.Windows.Input;
using System.Windows.Media;
using eProject.ViewModel;
using eProject.ViewModel.Commands;
using Binding = System.Windows.Data.Binding;
using ICommand=eProject.ViewModel.Commands.ICommand;
using MenuItem=System.Windows.Controls.MenuItem;
using MessageBox = System.Windows.MessageBox;
using ScrollBar = System.Windows.Controls.Primitives.ScrollBar;
using ScrollEventArgs = System.Windows.Controls.Primitives.ScrollEventArgs;

namespace eProject.View
{
    /// <summary>
    /// Interaction logic for NewMainWindow.xaml
    /// </summary>
    public partial class MainWindow
    {

        #region Constructors


        public MainWindow()
            : this(null)
        {
            //Do Nothing
        }

        public MainWindow(string path)
        {

            CommandsQueue = new CommandsQueue();

            Settings = new ApplicationSettings();
            Settings.Load();
            if (!String.IsNullOrEmpty(path))
            {
                _currentDataFileName = path;
                _isNewAndEmpty = false;
            }

            var cultureName = Settings.Culture;
            if (!String.IsNullOrEmpty(cultureName))
            {
                var culture = CultureInfo.GetCultureInfo(cultureName);
                System.Threading.Thread.CurrentThread.CurrentUICulture = culture;
                System.Threading.Thread.CurrentThread.CurrentCulture = culture;
            }

            if (Properties.Settings.Default.RecentProjects == null)
                Properties.Settings.Default.RecentProjects = new StringCollection();

            var recentProjects = Properties.Settings.Default.RecentProjects;
            RecentProjects = recentProjects.Cast<string>().Take(RecentProjectsMaxCount).ToList();

            if (_currentDataFileName != null) AddToRecentProjects(_currentDataFileName);


            Settings.PropertyChanged += SettingsPropertyChanged;

            InitializeComponent();

            if (!String.IsNullOrEmpty(_currentDataFileName))
            {
                FileStream stream = null;
                try
                {
                    stream = new FileStream(_currentDataFileName, FileMode.Open);
                    _manager = new CommonManager(stream);
                }
                catch (Exception)
                {
                    ShowErrorMessage(Properties.Resources.ErrorOpeningFile + _currentDataFileName);
                    _manager = new CommonManager();
                }
                finally
                {
                    if (stream!=null)
                    {
                        stream.Close();
                    }
                }
            }
            else
            {
                _manager = new CommonManager();
            }

            Manager = _manager;

            activitiesView1.LinkTo(_manager, CommandsQueue);
            activitiesView1.SetViewMode(ActivitiesView.Modes.ByTasks);
            activitiesView2.LinkTo(_manager, CommandsQueue);
            activitiesView2.SetViewMode(ActivitiesView.Modes.ByEmployees);
            

            if (_manager.Tasks.Count != 0)
            {
                var task = _manager.Tasks[0];
                activitiesView1.ShowBy(task);
            }
            if (_manager.Employees.Count != 0)
            {
                var employee = _manager.Employees[0];
                activitiesView2.ShowBy(employee);
            }

            activitiesView1.IsDetailed = Settings.IsDetailMode;
            activitiesView2.IsDetailed = Settings.IsDetailMode;


            BindScrollBar();          

            RefreshTitle();
            CommandsQueue.PropertyChanged += delegate(object sender, PropertyChangedEventArgs args)
                                                  {
                                                      if (args.PropertyName == "Length")
                                                      {
                                                          _isUnsaved = true;
                                                          RefreshTitle();
                                                          _isNewAndEmpty = false;
                                                      }
                                                  };
            AddListenersToCollections();

#if DEBUG
            CommandBindings.Add(new CommandBinding(Commands.StartGCCommand, CommandsBindingGcCommand));
            var serviceMenu = new MenuItem {Header = "Service"};
            serviceMenu.Items.Add(new MenuItem() {Header = "Start GC", Command = Commands.StartGCCommand});
            mainMenu.Items.Add(serviceMenu);

#endif

        }

        #endregion

        #region Properties

        private static readonly DependencyPropertyKey ManagerPropertyKey = DependencyProperty.RegisterReadOnly("Manager",
                                                                                                        typeof (
                                                                                                            CommonManager
                                                                                                            ),
                                                                                                        typeof (
                                                                                                            MainWindow),
                                                                                                        new PropertyMetadata
                                                                                                            (null));

        private static readonly DependencyPropertyKey CommandsQueuePropertykey =
            DependencyProperty.RegisterReadOnly("CommandsQueue", typeof (CommandsQueue), typeof (MainWindow),
                                                new PropertyMetadata(null));

        public CommandsQueue CommandsQueue
        {
            get { return (CommandsQueue)GetValue(CommandsQueuePropertykey.DependencyProperty); }
            private set { SetValue(CommandsQueuePropertykey, value); }   
        }

        public CommonManager Manager 
        {
            get { return (CommonManager)GetValue(ManagerPropertyKey.DependencyProperty); }
            private set{ SetValue(ManagerPropertyKey, value); }
        }

        public IEnumerable<string> RecentProjects { get; private set; }

        public ApplicationSettings Settings { get; private set; }

        #endregion

        #region Private helpers


        private void AddListenersToCollections()
        {
            INotifyCollectionChanged icce = Manager.Employees;
            icce.CollectionChanged += delegate(object sender, NotifyCollectionChangedEventArgs e)
            {
                if (Manager.Employees.Count == 1 ||
                    e.OldItems != null && e.OldItems.Count > 0
                    && Manager.Employees.Count > 0 &&
                    e.OldItems.Contains(activitiesView2.Employee))
                {
                    activitiesView2.ShowBy(Manager.Employees[0]);
                }
            };

            INotifyCollectionChanged icct = Manager.Tasks;
            icct.CollectionChanged += delegate(object sender, NotifyCollectionChangedEventArgs e)
            {
                if (Manager.Tasks.Count == 1 ||
                    e.OldItems != null && e.OldItems.Count > 0
                    && Manager.Tasks.Count > 0 &&
                    e.OldItems.Contains(activitiesView1.Task))
                {
                    activitiesView1.ShowBy(Manager.Tasks[0]);
                }
            };

        }

        private static MessageBoxResult ShowConfirmSaveDialog()
        {
            return MessageBox.Show(Properties.Resources.ConfirmSaveQuestion, Properties.Resources.ConfirmSaveHeader,
                            MessageBoxButton.YesNoCancel, MessageBoxImage.Question);
        }

        private static void ShowErrorMessage(string errorDescription)
        {
            MessageBox.Show(errorDescription, Properties.Resources.ErrorCaption, MessageBoxButton.OK,
                           MessageBoxImage.Error);
        }

        private static void ShowNotification(string message)
        {
            MessageBox.Show(message, Properties.Resources.AttentionHeader,
                            MessageBoxButton.OK, MessageBoxImage.Information);
        }

        private static DependencyObject GetScrollViewer(DependencyObject o)
        {
            // Return the DependencyObject if it is a ScrollViewer
            if (o is ScrollViewer)
            { return o; }

            for (var i = 0; i < VisualTreeHelper.GetChildrenCount(o); i++)
            {
                var child = VisualTreeHelper.GetChild(o, i);

                var result = GetScrollViewer(child);
                if (result == null)
                {
                    continue;
                }
                return result;
            }

            return null;
        }

        private void SaveState(string path)
        {
            FileStream stream = null;
            try
            {
                stream = new FileStream(path, FileMode.Create);
                _manager.Save(stream);
                _isUnsaved = false;
                RefreshTitle();
                _isNewAndEmpty = false;
            } finally
            {
                if (stream != null)
                {
                    stream.Close();
                }
                ShowErrorMessage("");
            }
            
            
        }

        private void RefreshTitle()
        {
            var result = ApplicationName;
            if (!String.IsNullOrEmpty(_currentDataFileName))
            {
                result += " - " + _currentDataFileName;
            }
            else
            {
                result += " - " + Properties.Resources.MainWindowTitleUnsavedDocument;
            }
            if (_isUnsaved)
            {
                result += "*";
            }
            Title = result;
        }

        private static void AddToRecentProjects(string path)
        {
            //if (Properties.Settings.Default.RecentProjects == null)
            //{
            //    Properties.Settings.Default.RecentProjects = new StringCollection();
            //}
            var recentProjects = Properties.Settings.Default.RecentProjects;
            if (recentProjects.Contains(path))
            {
                recentProjects.Remove(path);
            }
            recentProjects.Insert(0, path);
            if (recentProjects.Count > RecentProjectsMaxCount) recentProjects.RemoveAt(RecentProjectsMaxCount);
            SettingsBase.Synchronized(Properties.Settings.Default).Save();

        }

        private void BindScrollBar()
        {

            var bind1 = new Binding("ScrollWidth") { Source = activitiesView1 };
            scrollBar1.SetBinding(RangeBase.MaximumProperty, bind1);

            var bind2 = new Binding("ActualWidth") { Source = activitiesView1 };
            scrollBar1.SetBinding(ScrollBar.ViewportSizeProperty, bind2);

            var bind3 = new Binding("ScrollWidth") { Source = activitiesView2 };
            scrollBar2.SetBinding(RangeBase.MaximumProperty, bind3);

            var bind4 = new Binding("ActualWidth") { Source = activitiesView2 };
            scrollBar2.SetBinding(ScrollBar.ViewportSizeProperty, bind4);

        }

        #endregion

        #region Event Handlers

        private void MainWindow_OnWindowClosed(object sender, CancelEventArgs args)
        {
            if (_isUnsaved)
            {
                var result = ShowConfirmSaveDialog();

                if (result == MessageBoxResult.Cancel)
                {
                    args.Cancel = true;
                    return;
                }
                if (result == MessageBoxResult.Yes)
                {
                    SaveState(_currentDataFileName);
                }
            }
            Settings.Save();//TODO save settings more frequently?
            SettingsBase.Synchronized(Properties.Settings.Default).Save();//TODO synchronization

        }

        private void scrollBar1_Scroll(object sender, ScrollEventArgs e)
        {
            var scroll = GetScrollViewer(activitiesView1.listBox) as ScrollViewer;

            if (scroll == null) return;
            scroll.ScrollToHorizontalOffset(e.NewValue);
        }

        private void scrollBar2_Scroll(object sender, ScrollEventArgs e)
        {
            var scroll = GetScrollViewer(activitiesView2.listBox) as ScrollViewer;

            if (scroll == null) return;
            scroll.ScrollToHorizontalOffset(e.NewValue);
        }

        private void taskView1_MouseDoubleClick(object sender, MouseButtonEventArgs e)
        {
            var item = taskView1.listBox.SelectedItem;
            if (item == null) return;
            var task = item as TaskViewModel;
            if (task == null) throw new Exception("Incorrect type of the item");

            activitiesView1.ShowBy(task);
        }

        private void employeesView2_MouseDoubleClick(object sender, MouseButtonEventArgs e)
        {
            var item = employeesView2.listBox.SelectedItem;
            if (item == null) return;
            var empl = item as EmployeeViewModel;
            activitiesView2.ShowBy(empl);
        }

        private void CommandsBinding_NewCommand(object sender, ExecutedRoutedEventArgs e)
        {
            new MainWindow().Show();
        }



        private void CommandsBinding_SaveAsCommand(object sender, ExecutedRoutedEventArgs e)
        {
            var dialog = new SaveFileDialog();
            var result = dialog.ShowDialog();

            if (result == System.Windows.Forms.DialogResult.OK)
            {
                _currentDataFileName = dialog.FileName;
                ApplicationCommands.Save.Execute(null, this);
            }
        }

        private void CommandsBinding_OpenCommand(object sender, ExecutedRoutedEventArgs e)
        {
            var dialog = new OpenFileDialog { CheckFileExists = true };
            var result = dialog.ShowDialog();
            if (result == System.Windows.Forms.DialogResult.OK)
            {
                var path = dialog.FileName;
                new MainWindow(path).Show();

                if (_isNewAndEmpty) Close();
            }
        }


        private void CommandBinding_ExitCommand(object sender, ExecutedRoutedEventArgs e)
        {
            Close();
        }

        private void CommandBinding_UndoCommand(object sender, ExecutedRoutedEventArgs e)
        {
            CommandsQueue.Undo();
        }

        private void CommandBinding_UndoCanExecute(object sender, CanExecuteRoutedEventArgs e)
        {
            e.CanExecute = CommandsQueue.CanUndo;//TODO check this
        }

        private void CommandBinding_RedoCommand(object sender, ExecutedRoutedEventArgs e)
        {
            CommandsQueue.Redo();
        }

        private void CommandsBinding_SaveCommand(object sender, ExecutedRoutedEventArgs e)
        {
            if (_currentDataFileName == null)
            {
                ApplicationCommands.SaveAs.Execute(null, this);
                return;
            }

            AddToRecentProjects(_currentDataFileName);
            SaveState(_currentDataFileName);
        }

        private void CommandBinding_RedoCanExecute(object sender, CanExecuteRoutedEventArgs e)
        {
            e.CanExecute = CommandsQueue.CanRedo;
        }

        private void CommandsBinding_OpenRecentProjectCommand(object sender, ExecutedRoutedEventArgs e)
        {
            var param = e.Parameter as string;
            if (param == null) return;
            new MainWindow(param).Show();
            if (_isNewAndEmpty) Close();
        }

        private void SettingsPropertyChanged(object sender, PropertyChangedEventArgs args)
        {
            if (args.PropertyName == "IsDetailMode")
            {
                if (Settings.IsDetailMode)
                {
                    activitiesView1.IsDetailed = true;
                    activitiesView2.IsDetailed = true;//TODO replace this by binding
                }
                else
                {
                    activitiesView1.IsDetailed = false;
                    activitiesView2.IsDetailed = false;
                }
            }
            else if (args.PropertyName == "Culture")
            {
                //Properties.Settings.Default.UICulture = Settings.Culture;
                ShowNotification(Properties.Resources.NeedRestartMessage);
            }
        }

        private void activitiesView1_MouseUp(object sender, MouseButtonEventArgs e)
        {
            var item = activitiesView1.listBox.SelectedItem as ActivityViewModel;
            if (item == null)
            {
                statusBarText.Text = "";
                return;
            }
            statusBarText.Text = item.Task.Name + " - " + item.Employee.Name + " " + item.Employee.Surname + " - " +
                                 item.ManHours + " " + Properties.Resources.StatusBarTextManHours +
                                 (item.Description != "" ? (" - " + item.Description) : "");
        }

        private void taskView1_MouseUp(object sender, MouseButtonEventArgs e)
        {
            var item = taskView1.listBox.SelectedItem as TaskViewModel;
            if (item == null)
            {
                statusBarText.Text = "";
                return;
            }
            statusBarText.Text = item.Name + " " + item.Progress.ToString("#.0") + "% " + Properties.Resources.StatusBarTextCompleted + " (" + item.ManHours + " " + Properties.Resources.StatusBarTextOf + " " + item.Duration + " " + Properties.Resources.StatusBarTextHours + ")"
                + (item.Description != "" ? (" - " + item.Description) : "");
        }

        private void activitiesView2_MouseUp(object sender, MouseButtonEventArgs e)
        {
            var item = activitiesView2.listBox.SelectedItem as ActivityViewModel;
            if (item == null)
            {
                statusBarText.Text = "";
                return;
            }
            statusBarText.Text = item.Task.Name + " - " + item.Employee.Name + " " + item.Employee.Surname + " - " +
                                 item.ManHours + " " + Properties.Resources.StatusBarTextManHours +
                                 (item.Description != "" ? (" - " + item.Description) : "");
        }

        private void employeesView2_MouseUp(object sender, MouseButtonEventArgs e)
        {
            var item = employeesView2.listBox.SelectedItem as EmployeeViewModel;
            if (item == null)
            {
                statusBarText.Text = "";
                return;
            }
            statusBarText.Text = item.Name + " " + item.Surname + ", " + Properties.Resources.StatusBarTextEmail + ": " + item.EMail + (item.PhoneNumber != "" ? ", " + Properties.Resources.StatusBarTextPhone + ": " +
                item.PhoneNumber : "");
        }


        private void CommandsBinding_OpenScheduleSettingsCommand(object sender, ExecutedRoutedEventArgs args)
        {
            var mondayLenght = _manager.GetBusinessDayLength(DayOfWeek.Monday);
            var tuesdayLength = _manager.GetBusinessDayLength(DayOfWeek.Tuesday);
            var wednesdayLength = _manager.GetBusinessDayLength(DayOfWeek.Wednesday);
            var thursdayLength = _manager.GetBusinessDayLength(DayOfWeek.Thursday);
            var fridayLength = _manager.GetBusinessDayLength(DayOfWeek.Friday);
            var saturedayLength = _manager.GetBusinessDayLength(DayOfWeek.Saturday);
            var sundayLength = _manager.GetBusinessDayLength(DayOfWeek.Sunday);
            var dialog = new ScheduleSettingsDialog(_manager)
                             {
                                 MondayLenght = mondayLenght,
                                 TuesdayLength = tuesdayLength,
                                 WednesdayLength = wednesdayLength,
                                 ThursdayLength = thursdayLength,
                                 FridayLength = fridayLength,
                                 SaturedayLength = saturedayLength,
                                 SundayLength = sundayLength,

                                 Monday = mondayLenght != 0,
                                 Tuesday = tuesdayLength != 0,
                                 Wednesday = wednesdayLength != 0,
                                 Thursday = thursdayLength != 0,
                                 Friday = fridayLength != 0,
                                 Satureday = saturedayLength != 0,
                                 Sunday = sundayLength != 0,
                                 Owner = this
                             };
            var res = dialog.ShowDialog();
            if (res == true)
            {
                var list = new LinkedList<ICommand>();

                var mo = !dialog.Monday ? 0 : dialog.MondayLenght;
                var tu = !dialog.Tuesday ? 0 : dialog.TuesdayLength;
                var we = !dialog.Wednesday ? 0 : dialog.WednesdayLength;
                var th = !dialog.Thursday ? 0 : dialog.ThursdayLength;
                var fr = !dialog.Friday ? 0 : dialog.FridayLength;
                var sa = !dialog.Satureday ? 0 : dialog.SaturedayLength;
                var su = !dialog.Sunday ? 0 : dialog.SundayLength;
                var chSettingsCommand = new ChangeSettingsCommand(_manager,
                                                                  new List<double> {mo, tu, we, th, fr, sa, su});
                list.AddLast(chSettingsCommand);
                var scitems = dialog.ScheduleItems;
                var newItems = scitems.Except(_manager.ScheduleItems);
                var oldItems = _manager.ScheduleItems.Except(scitems);

                var items = scitems.Except(newItems).Except(oldItems);

                foreach (var item in newItems)
                {
                    var command = new CreateScheduleItemCommand(_manager, item);
                    list.AddLast(command);
                }
                foreach (var item in oldItems)
                {
                    var command = new DeleteScheduleItemCommand(_manager, item);
                    list.AddLast(command);
                }

                foreach (var item in items)
                {
                    var oldIndex = _manager.ScheduleItems.IndexOf(item);
                    var newIndex = scitems.IndexOf(item);
                    if (oldIndex != newIndex)
                    {
                        var command = new MoveScheduleItemCommand(_manager, item, newIndex);
                        list.AddLast(command);
                    }
                }

                var grpcommand = new GroupCommand(list);

                CommandsQueue.Do(grpcommand);
            }
        }

        private void CommandsBinding_ShowAboutBoxCommand(object sender, ExecutedRoutedEventArgs e)
        {
            new AboutBox { Owner = this }.ShowDialog();
        }

#if DEBUG
        private static void CommandsBindingGcCommand(object sender, ExecutedRoutedEventArgs e)
        {
            GC.Collect();
            GC.WaitForPendingFinalizers();
            GC.Collect();
        }
#endif

        #endregion

        #region Fields

        private string _currentDataFileName;

        private readonly CommonManager _manager;

        private bool _isNewAndEmpty = true;

        private bool _isUnsaved;

       

        private const string ApplicationName = "eProject";

        private const int RecentProjectsMaxCount = 5;

        #endregion

        
    }

#region Value Converter

    public class BoolToLocaleConverter : IValueConverter
    {
        public static BoolToLocaleConverter Instance = new BoolToLocaleConverter();

        public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
        {
            var val = value as String;
            if (val == null) return null;
            var param = parameter as string;
            if (param == null) return null;

            return val.Equals(param);
        }

        public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
        {
            var val = (bool)value;
            var param = parameter as string;
            return val ? param : null;
        }
    }

#endregion

}
